背景
最近一个月一直在做快速扫描磁盘文件的东西,主要是通过扫描NTFS格式的磁盘中的索引树(B-树,经过我查证,应该是B-树),可以得到整个磁盘文件的索引,然后通过遍历其索引树,即可得到目录下有哪些文件。这个需要对NTFS格式的磁盘的内部数据结构有一定的了解,这个可以参考我的另外一篇文章(这里还没有完成,待我完成时发布出来,当然网上也有很多资料)。
预期是希望能够比windows的API(FindFirstFile和FindNextFile)效率要高,但是实际结果却不好,原因是磁盘IO太多了,因为首先找到根目录,然后找到根目录下的文件以及目录,然后再通过目录寻找其目录下的文件以及文件夹,所以需要不断的磁盘IO。
这里我使用的文件映射(CreateFileMapping好像不可以映射磁盘)、异步IO以及windows的完成端口(IOCP),最终的效果都不好。
然后经过老大的提醒,DeviceIOControl这个API也可以进行磁盘操作。查阅MSDN,这个API功能比较强大,功能比较多,发现其可以通过控制码直接和驱动交互,读取磁盘的File Entry。
因为它的效率比较高,最终采用的读取磁盘所有File Entry,而后自建索引树。
API详细介绍
API参数
|
|
参数一:设备句柄
参数二:控制码
参数三:输入缓冲区
参数四:输入缓冲区长度
参数五:输出缓冲区
参数六:输出缓冲区长度
参数七:返回的字节长度
参数八:一个OverLapped结构的指针
控制码
关于控制码主要有以下几个主题:
控制码比较多,这里只介绍读取MFT的控制码,其他有兴趣可以参考MSDN。这个控制是File Management Control Codes中的 FSCTL_GET_NTFS_FILE_RECORD。
这个控制在MSDN中的解释是Retrieves the first file record that is in use and is of a lesser than or equal ordinal value to the requested file reference number.说实话,我也没有完全理解这句话,暂且理解成通过file reference number获得file entry。
控制码对应的API参数
再看该控制码对应的API参数结构:
这里主要注意两个参数:
参数三:NTFS_FILE_RECORD_INPUT_BUFFER 结构指明你要读取的File Entry的编号,例如$MFT的编号为0,那么你就给该参数传入0,需要注意的是LARGER_INTEGER有64位,分为高32位和低32位。
参数五:NTFS_FILE_RECORD_OUTPUT_BUFFER 结构,用来保存读取的数据缓冲区。注意,FileRecordBuffer只占一个字节,如果数据没有读完,该API返回相应的提示。一般一个File Entry占2个扇区,一个扇区一般512字节,所以这里我一般将FileRecordBuffer改为占用1024字节的数组。
相关代码
以上就是利用DeviceIoControl1读取File Entry时需要注意的地方,下面看下完整代码:
首先打开磁盘,注意CreateFile的各个参数的含义:
然后读取File Entry:
效率
从我最终的测试结果来看,利用DeviceIoControl的控制码读取MFT的效率是非常快的。
- 操作系统:windows 7 64 bit
- 内存:8G
- 测试对象:系统盘(C盘),大概有45万个文件
扫描系统盘所有File Entry,并且将读取的所有File Entry自建索引树,总共用时 2秒 不到。所以这个效率还是很高的。至少比ReadFile来读取MFT的效率要高。
如果本文对你有用,请留下你来过的痕迹,转载请注明出处[https://soygrow.github.io]!2016年8月4号于北京